#ifndef TYPE_CHECKER_HPP
#define TYPE_CHECKER_HPP

#include "type.hpp"
#include "visitor.hpp"

#include <string>
using namespace std;

class SymbolTable;

class TypeChecker : public Visitor {
private:
    Type* _type;
    SymbolTable* _sym_table;
    TypeTable*   _type_table;

    bool    _has_error;
    bool    _is_func;

public:
    TypeChecker(SymbolTable* symtable, bool is_func) : _sym_table(symtable), 
    _has_error(false), _type(nullptr), _is_func(is_func) {
        _type_table = new TypeTable();
    }

    ~TypeChecker() {
        if (_type_table) {
            delete _type_table;
            _type_table = nullptr;
        }
    }

    bool has_error();
    SymbolTable* sym_table()    { return _sym_table; }

    Type* expr_type()           { return _type; }

    template<bool is_func>
    void check_name(const string& name, Type* t);

    Type* get_param_type(ParamDefNode* n);
    Type* build_func_type(FuncDefNode* n);
    void  fill_sym_table(FuncDefNode* n);
    void  instantiate_generic_func(FuncDefNode* n);

    void visit(Node* n);

    virtual void visit(PrintNode* n);
    virtual void visit(ReturnNode* n);
    virtual void visit(BinaryOp* n);
    virtual void visit(LogicNotNode* n);
    virtual void visit(ListNode* n);
    virtual void visit(ConstInt* n);
    virtual void visit(ConstString* n);
    virtual void visit(ConstArray* n);
    virtual void visit(IfNode* n);
    virtual void visit(WhileNode* n);
    virtual void visit(CmpNode* n);
    virtual void visit(AssignNode* n);
    virtual void visit(VarNode* n);
    virtual void visit(VarDefNode* n);
    virtual void visit(ParamDefNode* n);
    virtual void visit(FuncDefNode* n);
    virtual void visit(LambdaDef* n);
    virtual void visit(CallNode* n);
    virtual void visit(SubscrNode* n);
    virtual void visit(BuiltinConst* n);

    virtual void visit(ArrowTypeNode* n);
    virtual void visit(ArrayTypeNode* n);

    virtual void visit(TypeVarNode* n);
    virtual void visit(TypeFuncDefNode* n);
    virtual void visit(TypeVarDefNode* n);
    virtual void visit(TypeCallNode* n);
    virtual void visit(TypeConstInt* n);
};

enum ConstValueType {
    CVT_INT,
    CVT_DOULE,
    CVT_STRING,
    CVT_CODE_OBJECT,
    CVT_UNIT,
    CVT_TRUE,
    CVT_FALSE,
};

union ValueUnion {
    long long int_value;
    string* str_value;
    double dbl_value;
};

class EvaluatorValue {
public:
    ConstValueType cvt;
    ValueUnion value;
};

class Evaluator : public Visitor {
private:
    TypeChecker*    _type_checker;
    EvaluatorValue  _value;

public:
    Evaluator(TypeChecker* checker) : _type_checker(checker) {
    }

    long long int_value() {
        return _value.value.int_value;
    }

    void visit(Node* n);

    virtual void visit(PrintNode* n) {}
    virtual void visit(ReturnNode* n) {}
    virtual void visit(BinaryOp* n) {}
    virtual void visit(LogicNotNode* n) {}
    virtual void visit(ListNode* n) {}
    virtual void visit(ConstInt* n) {};
    virtual void visit(ConstString* n) {}
    virtual void visit(ConstArray* n) {}
    virtual void visit(IfNode* n) {}
    virtual void visit(WhileNode* n) {}
    virtual void visit(CmpNode* n) {}
    virtual void visit(AssignNode* n) {}
    virtual void visit(VarNode* n) {}
    virtual void visit(VarDefNode* n) {}
    virtual void visit(ParamDefNode* n) {}
    virtual void visit(FuncDefNode* n) {}
    virtual void visit(LambdaDef* n) {}
    virtual void visit(CallNode* n) {}
    virtual void visit(SubscrNode* n) {}
    virtual void visit(BuiltinConst* n) {}

    virtual void visit(ArrowTypeNode* n) {}
    virtual void visit(ArrayTypeNode* n) {}

    virtual void visit(TypeVarNode* n) {}
    virtual void visit(TypeFuncDefNode* n) {}
    virtual void visit(TypeVarDefNode* n) {}
    virtual void visit(TypeCallNode* n) {}
    virtual void visit(TypeConstInt* n);
};

#endif

